package dal

import (
	"context"
	"fmt"
	"github.com/shopspring/decimal"
	"lianzi_gos/internal/mods/mpweixin/schema"
	"lianzi_gos/pkg/errors"
	"lianzi_gos/pkg/util"
	"time"

	"gorm.io/gorm"
)

// Get goods storage instance
func GetGoodsDB(ctx context.Context, defDB *gorm.DB) *gorm.DB {
	return util.GetDB(ctx, defDB).Model(new(schema.Goods))
}

// 商品表
type Goods struct {
	DB *gorm.DB
}

// Query goods from the database based on the provided parameters and options.
func (a *Goods) Query(ctx context.Context, params schema.GoodsQueryParam, opts ...schema.GoodsQueryOptions) (*schema.GoodsQueryResult, error) {
	var opt schema.GoodsQueryOptions
	if len(opts) > 0 {
		opt = opts[0]
	}

	db := a.DB.Table(fmt.Sprintf("%s AS g", new(schema.Goods).TableName())).
		Joins(fmt.Sprintf("left join %s a on a.code = g.code", new(schema.GoodsAttr).TableName())).
		Joins(fmt.Sprintf("left join %s c on c.id = a.cid", new(schema.Category).TableName())).
		Joins(fmt.Sprintf("left join %s b on b.id = a.bid", new(schema.Business).TableName())).
		Joins(fmt.Sprintf("left join %s m on m.uid = g.w_id", new(schema.Staff).TableName())).
		Select("g.publish_at,a.stock,g.two_image,g.two_name,a.cid as c_cid,c.name as cate_name," +
			"g.id,g.code,g.speed,g.types,a.price as price,g.sale,g.selection,g.address,g.province,g.district," +
			"g.city,a.name,a.unit,a.fineness,a.size,a.power,a.diameter,a.capacity,a.method,a.year,a.image," +
			"m.name as p_name,m.phone as p_phone,m.avatar as p_avatar,g.overrule_msg,g.overrule_at," +
			"sum(a.price * a.stock) as total_price,a.id as attr_id").Group("a.code")
	if v := params.StoreType; len(v) > 0 {
		db = db.Where("g.types = ?", v)
		if v == "two" {
			if v := params.Name; len(v) > 0 {
				db = db.Where("g.two_name LIKE ? or a.b_name LIKE ? ", "%"+v+"%", "%"+v+"%")
			}
		} else {
			if v := params.Name; len(v) > 0 {
				db = db.Where("c.name LIKE ? or a.name LIKE ? or g.two_name LIKE ? or a.b_name LIKE ? or g.code LIKE ? or b.name LIKE ?", "%"+v+"%", "%"+v+"%", "%"+v+"%", "%"+v+"%", "%"+v+"%", "%"+v+"%")
			}
		}
	} else {
		if v := params.Name; len(v) > 0 {
			db = db.Where("a.name LIKE ?  or c.name LIKE ? or  a.b_name LIKE ? or g.code LIKE ? or b.name LIKE ?", "%"+v+"%", "%"+v+"%", "%"+v+"%", "%"+v+"%", "%"+v+"%")
		}
	}

	if v := params.CityId; v > 0 {
		db = db.Where("g.district_id = ? ", v)
	}
	if v := params.Status; len(v) > 0 {
		db = db.Where("g.status = ? ", v)
	} else {
		db = db.Where("g.status = ? ", "enabled")
	}

	if v := params.BusId; len(v) > 0 {
		db = db.Where("g.bus_id = ? ", v)
	}
	if v := params.Cid; len(v) > 0 {
		db = db.Where("c.parent_id LIKE ? or c.id LIKE ?", params.Cid, params.Cid)
	}
	if v := params.Recommend; v == 2 {
		db = db.Where("g.recommend = ? ", v)
	}

	if v := params.PriceOrder; len(v) > 0 {
		if v == "desc" {
			db = db.Order("a.price desc")
		}
		if v == "asc" {
			db = db.Order("a.price asc")
		}
	}

	switch params.Price {
	case 1:
		db = db.Where("a.price BETWEEN ? AND ? ", 1, 100)
	case 2:
		db = db.Where("a.price BETWEEN ? AND ? ", 100, 1000)
	case 3:
		db = db.Where("a.price BETWEEN ? AND ? ", 1000, 5000)
	case 4:
		db = db.Where("a.price BETWEEN ? AND ? ", 5000, 10000)
	case 5:
		db = db.Where("a.price  > ? ", 10000)
	default:
	}
	db = db.Order("g.created_at desc")
	/**
	判断 来源
	resource = 1 首页 + 商品列表页
	resource = 2  普通用户 我的发布
	resource = 3  归属入  所看的
	*/

	if params.Source == 1 {
		db = db.Where("g.speed = 3").Where("g.status = ?", "enabled")
	} else if params.Source == 2 {
		if v := params.Speed; v == -1 || v >= 0 {
			db = db.Where("g.speed  = ?", v)
		}
		db = db.Where("g.uid = ?", params.UID)
	} else if params.Source == 3 {
		//if params.Speed == 1 {
		//	db = db.Where("g.w_id = ?", params.UID).Where("g.speed = ?", params.Speed)
		//} else if params.Speed > 1 {
		db = db.Where("g.w_id = ?", params.UID).Where("g.speed = ?", params.Speed)
		//} \
	} else if params.Source == 4 {
		db = db.Where("g.speed = ?", params.Speed)
	} else if params.Source == 5 {
		db = db.Where("g.speed >= ? and g.speed < ?", 1, 4)
	}

	var list schema.Goodss

	pageResult, err := util.WrapPageQuery(ctx, db, params.PaginationParam, opt.QueryOptions, &list)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	queryResult := &schema.GoodsQueryResult{
		PageResult: pageResult,
		Data:       list,
	}
	return queryResult, nil
}

// Query goods from the database based on the provided parameters and options.
func (a *Goods) QueryAdmin(ctx context.Context, params schema.GoodsQueryParam, opts ...schema.GoodsQueryOptions) (*schema.GoodsQueryResultAdmin, error) {
	var opt schema.GoodsQueryOptions
	if len(opts) > 0 {
		opt = opts[0]
	}

	db := a.DB.Table(fmt.Sprintf("%s AS g", new(schema.Goods).TableName())).
		Joins(fmt.Sprintf("left join %s a on a.code = g.code", new(schema.GoodsAttr).TableName())).
		Joins(fmt.Sprintf("left join %s m on m.uid = g.w_id", new(schema.Staff).TableName())).
		Select("a.stock,g.*,a.name,a.unit,a.fineness,a.length,a.height,a.width,a.year,a.image," +
			"m.name as p_name,m.phone as p_phone,m.avatar as p_avatar").Group("a.code")

	if v := params.Name; len(v) > 0 {
		db = db.Where("a.name LIKE ?", "%"+v+"%")
	}
	if v := params.Types; len(v) > 0 {
		db = db.Where("g.types = ?", v)
	}
	if v := params.CityId; v > 0 {
		db = db.Where("g.district_id = ? ", v)
	}
	if v := params.Recommend; v > 0 {
		db = db.Where("g.recommend = ? ", v)
	}
	if v := params.PriceOrder; len(v) > 0 {
		db = db.Order("g.price " + v)
	}
	if v := params.PriceOrder; len(v) > 0 {
		db = db.Order("g.price " + v)
	}

	switch params.Price {
	case 1:
		db = db.Where("g.price BETWEEN ? AND ? ", 1, 100)
	case 2:
		db = db.Where("g.price BETWEEN ? AND ? ", 100, 1000)
	case 3:
		db = db.Where("g.price BETWEEN ? AND ? ", 1000, 5000)
	case 4:
		db = db.Where("g.price BETWEEN ? AND ? ", 5000, 10000)
	case 5:
		db = db.Where("g.price  > ? ", 10000)
	default:
		db = db.Where("g.price  > ? ", 0)

	}
	/**
	判断 来源
	resource = 1 首页 + 商品列表页
	resource = 2  普通用户 我的发布
	resource = 3  归属入  所看的
	*/

	if params.Source == 1 {
		db = db.Where("g.speed = 2").Where("g.price > ?", 0)
	} else if params.Source == 2 {
		db = db.Where("g.uid = ?", params.UID)
	} else if params.Source == 3 {
		db = db.Where("g.w_id = ?", params.UID).Where("g.speed >= 1")
	}

	if params.Speed > 0 {
		db = db.Where("g.speed = ?", params.Speed)
	}

	var list schema.GoodsAdminWeb

	pageResult, err := util.WrapPageQuery(ctx, db, params.PaginationParam, opt.QueryOptions, &list)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	queryResult := &schema.GoodsQueryResultAdmin{
		PageResult: pageResult,
		Data:       list,
	}
	return queryResult, nil
}

// Get the specified goods from the database.
func (a *Goods) Get(ctx context.Context, id string, opts ...schema.GoodsQueryOptions) (*schema.Goods, error) {
	var opt schema.GoodsQueryOptions
	if len(opts) > 0 {
		opt = opts[0]
	}

	item := new(schema.Goods)
	ok, err := util.FindOne(ctx, GetGoodsDB(ctx, a.DB).Where("id=?", id), opt.QueryOptions, item)

	if err != nil {
		return nil, errors.WithStack(err)
	} else if !ok {
		return nil, nil
	}
	return item, nil
}

// Get the specified goods from the database.
func (a *Goods) GetDetail(ctx context.Context, id string, opts ...schema.GoodsQueryOptions) (*schema.GoodsDetail, error) {
	var opt schema.GoodsQueryOptions
	if len(opts) > 0 {
		opt = opts[0]
	}

	var item *schema.GoodsDetail

	db := a.DB.Table(fmt.Sprintf("%s AS a", new(schema.GoodsAttr).TableName())).
		Joins(fmt.Sprintf("left join %s g on a.code = g.code", new(schema.Goods).TableName())).
		Joins(fmt.Sprintf("left join %s m on m.uid = g.w_id", new(schema.Staff).TableName())).
		Joins(fmt.Sprintf("left join %s bus on g.bus_id = bus.id", new(schema.Business).TableName())).
		Joins(fmt.Sprintf("left join %s cate on cate.id = a.cid", new(schema.Category).TableName())).
		Where("a.id = ?", id).
		Select("g.two_image,g.publish_at,g.two_name,g.code,g.sale,g.selection,a.b_name,a.desc,bus.name as bus_name,cate.name as cate_name,a.stock," +
			"a.id,g.types,a.price,g.address,g.city,g.province,g.district,a.name,a.unit,a.fineness,a.size," +
			"a.power,a.diameter,a.capacity,a.method,a.year,a.image," +
			"m.name as p_name,m.phone as p_phone,m.avatar as p_avatar,g.username,g.phone," +
			"g.overrule_msg,g.overrule_at,g.speed,g.created_at,g.types,a.video_url,cate.id as cid,a.price_image")
	ok, err := util.FindOne(ctx, db, opt.QueryOptions, &item)
	if err != nil {
		return nil, errors.WithStack(err)
	} else if !ok {
		return nil, nil
	}
	return item, nil
}

// Get the specified goods from the database.
func (a *Goods) GetBelongingDetail(ctx context.Context, id string, opts ...schema.GoodsQueryOptions) (*schema.GoodsBelongingDetail, error) {

	var opt schema.GoodsQueryOptions
	if len(opts) > 0 {
		opt = opts[0]
	}
	var item *schema.GoodsBelongingDetail

	db := a.DB.Table(fmt.Sprintf("%s AS g", new(schema.Goods).TableName())).
		Joins(fmt.Sprintf("left join %s a on a.code = g.code", new(schema.GoodsAttr).TableName())).
		Joins(fmt.Sprintf("left join %s m on m.uid = g.w_id", new(schema.Staff).TableName())).
		Where("g.id = ?", id).
		Select("g.*," +
			"m.name as p_name,m.phone as p_phone,m.avatar as p_avatar").Omit("goods_attr")
	ok, err := util.FindOne(ctx, db, opt.QueryOptions, &item)
	if err != nil {
		return nil, errors.WithStack(err)
	} else if !ok {
		return nil, nil
	}
	return item, nil
}

// Exist checks if the specified goods exists in the database.
func (a *Goods) Exists(ctx context.Context, id string) (bool, error) {
	ok, err := util.Exists(ctx, GetGoodsDB(ctx, a.DB).Where("id=?", id))
	return ok, errors.WithStack(err)
}

// Create a new goods.
func (a *Goods) Create(ctx context.Context, item *schema.Goods) error {
	result := GetGoodsDB(ctx, a.DB).Create(item)
	return errors.WithStack(result.Error)
}

// Update the specified goods in the database.
func (a *Goods) Update(ctx context.Context, item *schema.Goods) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", item.ID).Select("*").Omit("created_at").Updates(item)
	return errors.WithStack(result.Error)
}

// 完成订单
func (a *Goods) UpdateSuccess(ctx context.Context, id, contract, contract_desc, m_name, m_phone string, true_price decimal.Decimal, speed int64, success_at time.Time) error {

	result := GetGoodsDB(ctx, a.DB).Where("id= ?", id).
		Updates(schema.Goods{Contract: contract,
			ContractDesc: contract_desc,
			MPhone:       m_phone,
			MName:        m_name,
			TruePrice:    true_price,
			Speed:        speed, SuccessAt: success_at})
	return errors.WithStack(result.Error)
}

// 完成 商家信息
func (a *Goods) UpdateTwoSuccess(ctx context.Context, id string, item *schema.Goods) error {
	result := GetGoodsDB(ctx, a.DB).Where("id= ?", id).
		Updates(item)
	return errors.WithStack(result.Error)
}

// 驳回
func (a *Goods) UpdateOverrule(ctx context.Context, id, overrule string, speed int64, overrule_at time.Time) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Updates(schema.Goods{OverruleMsg: overrule,
			Speed: speed, OverruleAt: overrule_at})
	return errors.WithStack(result.Error)
}

// 更新估价
func (a *Goods) UpdatePrice(ctx context.Context, id string, price decimal.Decimal) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Updates(schema.Goods{Price: price})
	return errors.WithStack(result.Error)
}

// 指派
func (a *Goods) UpdateApproved(ctx context.Context, id, zhipaiid, suid string, speed int64, approved_at time.Time) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Updates(schema.Goods{WID: suid, Speed: speed, ApprovedAt: approved_at, ZhiAt: approved_at, ZUID: zhipaiid})
	return errors.WithStack(result.Error)
}

// 发布
func (a *Goods) UpdatePublish(ctx context.Context, id string, speed int64, pub_at time.Time) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Updates(schema.Goods{Speed: speed, PublishAt: pub_at})
	return errors.WithStack(result.Error)
}

// 精选
func (a *Goods) UpdateSelection(ctx context.Context, id string, selection int64) error {

	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Update("selection", selection)
	return errors.WithStack(result.Error)
}

// 推荐首页
func (a *Goods) UpdateRecommend(ctx context.Context, id string, recommend int64) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Update("recommend", recommend)
	return errors.WithStack(result.Error)
}

// 上下架状态
func (a *Goods) UpdateStatus(ctx context.Context, id, status string) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Update("status", status)
	return errors.WithStack(result.Error)
}

// 急售
func (a *Goods) UpdateSale(ctx context.Context, id string, sale int64) error {

	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).
		Update("sale", sale)
	return errors.WithStack(result.Error)
}

// Delete the specified goods from the database.
func (a *Goods) Delete(ctx context.Context, id string) error {
	result := GetGoodsDB(ctx, a.DB).Where("id=?", id).Delete(new(schema.Goods))
	return errors.WithStack(result.Error)
}
